package com.stormragetech.arthas.serialize.convertor;


import com.stormragetech.arthas.common.constant.Constant;
import com.stormragetech.arthas.common.exception.ArthasException;
import com.stormragetech.arthas.common.util.Assert;
import lombok.extern.log4j.Log4j2;

import java.math.BigDecimal;

/**
 * description: convertor executor
 *
 * @author: Xue Bin
 * @version: 1.0.0-SNAPSHOT
 */
@Log4j2
public class ConvertProcessor<T> implements Convertor {

    private Class<T> clazz;
    private Object value;

    private T t;

    public ConvertProcessor(Class<T> clazz, Object value) throws ArthasException {
        Assert.notNull(clazz, "Class<T> clazz can not be null !!!");
        Assert.notNull(value, "Map<String,String> map can not be null !!!");
        this.clazz = clazz;
        this.value = value;
        conv();
    }

    private void conv() throws ArthasException {
        long start = System.currentTimeMillis();
        switch (classType()) {
            case ConvertorType.PRIMITIVE:
                convToPrimitive();
                break;
            case ConvertorType.ARRAY:
                t = null;
                break;
            case ConvertorType.LOCAL:
                // TODO
                break;
            case ConvertorType.ILLEGAL:
                throw new ArthasException("123", "123", ArthasException.APP_ERR);
        }
        log.debug("conv cost {}", System.currentTimeMillis() - start);
    }

    private int classType() {
        return type(clazz);
    }

    private void convToPrimitive() throws ArthasException {
        if (isInt()) {
            convToInt();
        } else if (isLong()) {
            convToLong();
        } else if (isDouble()) {
            convToDouble();
        } else if (isBoolean()) {
            convToBoolean();
        } else if (isDecimal()) {
            convToDecimal();
        } else if (isString()) {
            value();
        } else {
            throw new ArthasException(Constant.CONV_ERR_CODE, Constant.CONV_NOT_PRIMITIVE, ArthasException.APP_ERR);
        }
    }

    private int type(Class<?> clazz) {
        try {
            if (isPrimitive(clazz)) {
                return ConvertorType.PRIMITIVE;
            } else if (isArray(clazz)) {
                return ConvertorType.ARRAY;
            } else if (isIllegal(clazz)) {
                return ConvertorType.ILLEGAL;
            } else {
                return ConvertorType.LOCAL;
            }
        } catch (Exception e) {
            log.error("verify type occur excption !!! excption: {}", e);
            return ConvertorType.ILLEGAL;
        }
    }

    static boolean isPrimitive(Class<?> clazz) {
        for (Class<?> c : ConvertorType.PRIMITIVE_TYPE) {
            if (c.equals(clazz)) {
                return true;
            }
        }
        return false;
    }

    public static boolean isArray(Class<?> clazz) {
        if (clazz.isArray()) {
            for (Class<?> c : ConvertorType.ARRAY_TYPE) {
                if (c.equals(clazz)) {
                    return true;
                }
            }
        }
        return false;
    }

    private static boolean isIllegal(Class<?> clazz) {
        return clazz.isInterface() || clazz.isEnum() || clazz.isAnnotation();
    }

    private boolean isInt() {
        return clazz.equals(int.class) || clazz.equals(Integer.class);
    }

    private boolean isLong() {
        return clazz.equals(long.class) || clazz.equals(Long.class);
    }

    private boolean isDouble() {
        return clazz.equals(double.class) || clazz.equals(Double.class);
    }

    private boolean isBoolean() {
        return clazz.equals(boolean.class) || clazz.equals(Boolean.class);
    }

    private boolean isDecimal() {
        return clazz.equals(BigDecimal.class);
    }

    private boolean isString() {
        return clazz.equals(String.class);
    }

    private int convToInt() throws ArthasException {
        checkString();
        return Integer.valueOf(value());
    }

    private long convToLong() throws ArthasException {
        checkString();
        return Long.valueOf(value());
    }

    private double convToDouble() throws ArthasException {
        checkString();
        return Double.valueOf(value());
    }

    private boolean convToBoolean() throws ArthasException {
        checkString();
        return Boolean.valueOf(value());
    }

    private BigDecimal convToDecimal() throws ArthasException {
        checkString();
        return new BigDecimal(value());
    }

    private String value() {
        return (String) value;
    }

    private void checkString() throws ArthasException {
        if (!value.getClass().equals(String.class)) {
            throw new ArthasException(Constant.CONV_ERR_CODE, Constant.CONV_NOT_STRING, ArthasException.APP_ERR);
        }
    }

    @Override
    public T convert() throws ArthasException {
        return this.t;
    }
}
